package stud.mpgame.flash.scene;

import flash.Lib;
import stud.mpgame.flash.game.GameEventDispatcher;
import stud.mpgame.flash.game.GameWorld;

import flash.text.TextField;
import flash.text.TextFieldType;

import flash.display.Sprite;
import flash.display.DisplayObject;

import stud.mpgame.flash.IScene;

import stud.mpgame.network.NetworkMonitor;
import stud.mpgame.network.Message;
import stud.mpgame.network.MessageDescription;

import stud.mpgame.network.service.time.TimeService;

import stud.mpgame.snapshot.ASnapshot;
import stud.mpgame.snapshot.ISnapshot;
import stud.mpgame.snapshot.SnapshotManager;

import stud.mpgame.flash.net.description.MapInfoMessageDescription;
import stud.mpgame.flash.net.description.ReadyToGameMessageDescription;
import stud.mpgame.flash.net.description.TestSnapshotDescription;
import stud.mpgame.flash.net.description.GameWorldMessageDescription;

import stud.mpgame.flash.net.userobject.MapInfoUserObject;

import stud.mpgame.flash.net.command.ReadyToGameCommand;

import stud.mpgame.flash.map.JsonDataProvider;
import stud.mpgame.flash.map.GameMap;

import stud.mpgame.flash.test.TestSnapshot;

/**
 * ...
 * @author pavel[f.pavel91@gmail.com]
 */

class LoadingScene extends Sprite, implements IScene {
	
	public static inline var NAME = "LoadingScene";
	
	private static var DELAY:Int = 2000;
	private static var SERVER_TICK_TIME:Int = 50;// 100;// 25;

    private var loadingField:TextField;
	
    private var networkMonitor:NetworkMonitor;
	private var timeService:TimeService;
	private var snapshotManager:SnapshotManager;
	
	private var gameEventDispatcher:GameEventDispatcher;
	
    private var jsonDataProvider:JsonDataProvider;
	private var mapURL:String;
    
	private var endLoadingTime:Int;
	private var isMapLoadingFinished:Bool;
	private var isFirstServerSnapshotReceived:Bool;
	private var firstServerSnapshot:ISnapshot;
	private var firstSnapshotReceivedTime:Int;

	private var nickname:String;
	private var userID:Int;
	private var gameWorldView:GameWorldView;
	
	
	public function new() {
        super();
        
		loadingField = new TextField();
		loadingField.border = false;
		loadingField.type = TextFieldType.DYNAMIC;
		loadingField.width = 200;
		loadingField.height = 18;
		loadingField.text = "Loading...";
		loadingField.x = 10;
		loadingField.y = 10;
		
		addChild(loadingField);
	}
	
	public function getName():String {
		return NAME;
	}
	
	public function mapLoadingFinished():Void {
		this.isMapLoadingFinished = true;
	}
	
	public function init(data:Dynamic):Void {
		this.isMapLoadingFinished = false;
		this.isFirstServerSnapshotReceived = false;
		
        this.networkMonitor = data.networkMonitor;
		//warning: first messages with ping may be lost, but it's for the better
		this.timeService = new TimeService(networkMonitor);
		
		this.nickname = data.nickname;
		this.userID = data.userID;
		
        MessageDescription.registerDescription(MapInfoMessageDescription.getInstance());
        MessageDescription.registerDescription(ReadyToGameMessageDescription.getInstance());
		MessageDescription.registerDescription(TestSnapshotDescription.getInstance());
		MessageDescription.registerDescription(GameWorldMessageDescription.getInstance());
        
        networkMonitor.getMessageDispatcher().addMessageReceivedListener(MapInfoMessageDescription.getInstance(), mapInfoReceived);
		
		networkMonitor.getMessageDispatcher().addMessageReceivedListener(GameWorldMessageDescription.getInstance(), gameWorldReceived);
	}

	public function mapInfoReceived(message:Message) {
		trace("Map info received");
		
		var mapInfo:MapInfoUserObject = cast(message.getUserObject(MapInfoUserObject), MapInfoUserObject);
       	var mapURL:String = mapInfo.getURL();
		
		this.endLoadingTime = Lib.getTimer() + DELAY;

		gameWorldView = new GameWorldView(this, userID);  
		gameWorldView.draw(mapURL);
	}
	
	private function gameWorldReceived(message:Message) {
		var gameWorld:GameWorld = cast(message.getUserObject(GameWorld), GameWorld);
		firstSnapshotReceivedTime = Lib.getTimer();
		//Текущее время примерно равно времени первого снепшота + ping.
		
		gameEventDispatcher = new GameEventDispatcher();
		gameWorld.init(gameWorldView.getMyId(), gameEventDispatcher, gameWorldView.getGameMap());
		gameWorldView.initPlayers(gameWorld);
		
		this.isFirstServerSnapshotReceived = true;
		this.firstServerSnapshot = gameWorld;
		
        networkMonitor.getMessageDispatcher().removeMessageReceivedListener(GameWorldMessageDescription.getInstance(), gameWorldReceived);		
	}
	
	public function sentReadyMessage():Void {
        var readyMessage:Message = new Message(networkMonitor, ReadyToGameMessageDescription.getInstance());
		
        var readyToGame:ReadyToGameCommand = new ReadyToGameCommand(timeService.getServerTime(), nickname, userID);
        readyMessage.putUserObject(readyToGame);
		
        readyMessage.send();
		
		trace("Ready sent");
	}
    
	public function clear():Void {
        MessageDescription.unRegisterDescription(MapInfoMessageDescription.getInstance());
        MessageDescription.unRegisterDescription(ReadyToGameMessageDescription.getInstance());
		MessageDescription.unRegisterDescription(TestSnapshotDescription.getInstance());
		MessageDescription.unRegisterDescription(GameWorldMessageDescription.getInstance());
		
		//ToDO network library bug
		networkMonitor.getMessageDispatcher().removeMessageReceivedListener(MapInfoMessageDescription.getInstance(), mapInfoReceived);		
	}
	
	public function update(deltaTime:Int):Void {
		if (isMapLoadingFinished && Lib.getTimer() > endLoadingTime) {
			sentReadyMessage();
			timeService.stop();
			//ready to receive first snapshot, the if condition is false now
			isMapLoadingFinished = false;
			return;
		}
		
		if (isFirstServerSnapshotReceived) {
			//trace("SNAPSHOT TIME = " + testSnapshot.getTime() + " SERVER TIME = " + timeService.getServerTime());
			//trace("LOCAL SNAPSHOT TIME = " + snapshotManager.getLocalSnapshotTime() + " SERVER TIME = " + timeService.getServerTime());			
			//TODO WARNING
			//cast(firstServerSnapshot, ASnapshot).setTime(timeService.getServerTime()); //TODO!
			var dt = Lib.getTimer() - firstSnapshotReceivedTime;
			firstServerSnapshot.extrapolate(dt + networkMonitor.getProperty("ping"));
			
			this.snapshotManager = new SnapshotManager(SERVER_TICK_TIME, firstServerSnapshot);
			
			trace("switch to game scene");
			Main.getInstance().setScene(GameScene.NAME, { 
				networkMonitor: networkMonitor, 
				snapshotManager: snapshotManager, 
				timeService: timeService, 
				gameWorldView: gameWorldView,
				gameEventDispatcher: gameEventDispatcher } );			
		}
	}
	
	public function getContentRoot():DisplayObject {
		return this;
	}
	
}